package com.gagakuai.concurrent;

import com.gagakuai.concurrent.locks.Condition;
import com.gagakuai.concurrent.locks.ReentrantLock;

import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/*
 * @program: common-starters
 * @description: 周期定时任务执行框架
 * @author: houhong
 * @create: 2023-02-25 22:44
 */
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {


    @Override
    public ScheduledFuture<?> schedule(Runnable runnable, long delay, TimeUnit unit) {
        return null;
    }

    @Override
    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
        return null;
    }

    @Override
    public <V> ScheduledFuture<V> scheduledAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
        return null;
    }

    @Override
    public <V> ScheduledFuture<V> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
        return null;
    }


    /*
     * 周期性任务的有序队列，按触发时间先后排列任务，不到触发时间的任务无法被取出
     * <p>
     * 顺序无界（队列容量支持扩容到Integer.MAX_VALUE）延时阻塞队列，线程安全（锁）
     * <p>
     * 该容器的内部实现为【小顶堆】，存储着RunnableScheduledFuture类的定时任务
     */
    static class DelayedWorkqQueue extends AbstractQueue<Runnable> implements BlockingQueue<Runnable> {


        private static final int INITIAL_CAPACITY = 16;

        //存储队列元素
        private RunnableScheduledFuture<?>[] queue = new RunnableScheduledFuture[INITIAL_CAPACITY];
        //队列长度
        private int size;
        //记录首个被阻塞的线程
        private Thread leader;

        private final ReentrantLock lock = new ReentrantLock(true);
        //条件队列
        private final Condition available = lock.newCondition();

        /*▼ 入队/出队 ████████████████████████████████████████████████████████████████████████████████┓ */


        // 入队，线程安全。队满时扩容★
        @Override
        public void put(Runnable e) {
            offer(e);
        }


        @Override
        public boolean offer(Runnable runnable) {

            if (runnable == null) throw new NullPointerException();


            RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>) runnable;

            ReentrantLock lock = this.lock;

            lock.lock();

            try {
                //当前数量
                int i = size;

                if (i >= queue.length) {
                    // 扩容50%
                    grow();
                }


                //数量+1
                size = i + 1;


                //第一个任务
                if(i == 0){
                    queue[0] = e;
                    // 记录该定时任务在延时队列中的索引
                    setIndex(e, 0);
                }else {

                    // 插入。需要从小顶堆的结点i开始，向【上】查找一个合适的位置插入e
                    siftUp(i, e);
                }

                //如果入队的元素排在了队首
                if(queue[0]==e){

                    leader = null;
                    // 队头有新任务达到，唤醒阻塞线程
                    available.signal();
                }
            } finally {

                lock.unlock();
            }

            return  true;

        }


        /*
        *
        *  实现小顶堆
        *   从当前位置向上找位置
        */
        private void siftUp(int i, RunnableScheduledFuture<?> key) {

            int parent = (i - 1) >> 1;
            //找到父节点
           RunnableScheduledFuture<?>  partentNode  =  queue[parent];

           //待插入元素大于父元素
           if(key.compareTo(partentNode) >=0){
               return;
           }

            // 子结点保存父结点中的元素
            queue[i] = partentNode;
            setIndex(partentNode, i);


            // 向上搜寻合适的插入位置
            i = parent;
        }


        // 出队，线程安全。队空时阻塞
        @Override
        public RunnableScheduledFuture<?> take() throws InterruptedException {

            final ReentrantLock lock = this.lock;
            //不能中断
            lock.lockInterruptibly();

            for (; ; ) {

                // 获取队头任务first
                RunnableScheduledFuture<?> first = queue[0];

                //队列为空
                if (first == null) {
                    //阻塞，等待singal() 唤醒
                    available.await();
                } else {

                    // 队头任务获取距被触发还剩余的时间
                    long delay = first.getDelay(TimeUnit.NANOSECONDS);

                    //任务已经可以执行了
                    if (delay <= 0L) {

                    }

                }


            }

        }


        /*
         *  除并返回队头元素f
         */
        private RunnableScheduledFuture<?> finishPoll(RunnableScheduledFuture<?> f) {

            int s = --size;
            // 获取队尾元素
            RunnableScheduledFuture<?> x = queue[s];

            queue[s] = null;

            if (s != 0) {
                // 插入。需要从小顶堆的结点0开始，向【下】查找一个合适的位置插入x
                siftDown(0, x);
            }

            setIndex(f, -1);

            return f;
        }

        // 插入。需要从小顶堆的结点i开始，向【下】查找一个合适的位置插入key
        private void siftDown(int i, RunnableScheduledFuture<?> key) {
            // 最多搜索一半元素
            int half = size >>> 1;

            while (i < half) {
                int min = (i << 1) + 1;    // 左结点索引
                int right = min + 1;       // 右结点索引

                // 假设左结点为较小的结点
                RunnableScheduledFuture<?> m = queue[min];

                // 如果右结点更小
                if (right < size && m.compareTo(queue[right]) > 0) {
                    // 更新min指向子结点中较小的结点
                    m = queue[min = right];
                }

                // 如果待插入元素小于子结点中较小的元素，则退出循环
                if (key.compareTo(m) <= 0) {
                    break;
                }

                // 父结点位置保存子结点中较小的元素
                queue[i] = m;
                setIndex(m, i);

                // 向下搜寻合适的插入位置
                i = min;
            }

            // 将元素key插入到合适的位置
            queue[i] = key;
            setIndex(key, i);
        }

        // 更新定时任务的heapIndex域（记录定时任务在延时队列中的索引）
        private static void setIndex(RunnableScheduledFuture<?> f, int idx) {
            if (f instanceof ScheduledFutureTask) {
                ((ScheduledFutureTask) f).heapIndex = idx;
            }
        }

        /*
         * Finds index of given object, or -1 if absent.
         */
        // 获取元素x的索引
        private int indexOf(Object x) {
            if (x != null) {
                if (x instanceof ScheduledFutureTask) {
                    int i = ((ScheduledFutureTask) x).heapIndex;
                    // Sanity check; x could conceivably be a
                    // ScheduledFutureTask from some other pool.
                    if (i >= 0 && i < size && queue[i] == x) {
                        return i;
                    }
                } else {
                    for (int i = 0; i < size; i++) {
                        if (x.equals(queue[i])) {
                            return i;
                        }
                    }
                }
            }
            return -1;
        }

        /*
         * Resizes the heap array.  Call only when holding lock.
         */
        // 扩容50%
        private void grow() {
            int oldCapacity = queue.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容50%
            if (newCapacity < 0) { // 溢出
                newCapacity = Integer.MAX_VALUE;
            }
            queue = Arrays.copyOf(queue, newCapacity);
        }

    }


    private class ScheduledFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> {


        /*
         * 记录任务本身，方便后面重复执行
         */
        RunnableScheduledFuture<V> outerTask = this;

        /*
         * 当前任务/元素在延时队列（小顶堆）中的索引
         */
        int heapIndex;


        /*
         * 任务的重复模式：
         *
         * 零  ：非重复任务：只执行一次
         * 正数：重复性任务：【固定周期】，从任务初次被触发开始，以后每隔period时长就被触发一次(即使上次被触发的任务还未执行完)
         * 负数：重复性任务：【固定延时】，任务下次的开始时间=任务上次结束时间+|period|(必须等到上次的任务已经执行完)
         */
        private long priod;

        // 任务下次被触发的时间（绝对时间，时间单位是纳秒）
        private volatile long time;

        // 任务加入队列的次序
        private final long sequenceNumber;


        public ScheduledFutureTask(Callable<V> callable) {
            super(callable);
        }

        public ScheduledFutureTask(Runnable runnable, V result) {
            super(runnable, result);
        }

        @Override
        public boolean isPeriodic() {
            return false;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return 0;
        }

        @Override
        public int compareTo(Delayed o) {
            return 0;
        }

    }
}